use super::{RespValue, ProtocolResult};
use bytes::{BufMut, BytesMut};

/// RESP protocol encoder
pub struct RespEncoder;

impl RespEncoder {
    /// Encode a RESP value to bytes
    pub fn encode(value: &RespValue) -> ProtocolResult<BytesMut> {
        let mut buf = BytesMut::new();
        Self::encode_value(value, &mut buf)?;
        Ok(buf)
    }

    /// Encode a RESP value into the buffer
    fn encode_value(value: &RespValue, buf: &mut BytesMut) -> ProtocolResult<()> {
        match value {
            RespValue::SimpleString(s) => {
                buf.put_u8(b'+');
                buf.put_slice(s.as_bytes());
                buf.put_slice(b"\r\n");
            }
            RespValue::Error(s) => {
                buf.put_u8(b'-');
                buf.put_slice(s.as_bytes());
                buf.put_slice(b"\r\n");
            }
            RespValue::Integer(i) => {
                buf.put_u8(b':');
                buf.put_slice(i.to_string().as_bytes());
                buf.put_slice(b"\r\n");
            }
            RespValue::BulkString(Some(bytes)) => {
                buf.put_u8(b'$');
                buf.put_slice(bytes.len().to_string().as_bytes());
                buf.put_slice(b"\r\n");
                buf.put_slice(bytes);
                buf.put_slice(b"\r\n");
            }
            RespValue::BulkString(None) => {
                buf.put_slice(b"$-1\r\n");
            }
            RespValue::Array(Some(elements)) => {
                buf.put_u8(b'*');
                buf.put_slice(elements.len().to_string().as_bytes());
                buf.put_slice(b"\r\n");
                for element in elements {
                    Self::encode_value(element, buf)?;
                }
            }
            RespValue::Array(None) => {
                buf.put_slice(b"*-1\r\n");
            }
        }
        Ok(())
    }

    /// Encode multiple values
    pub fn encode_array(values: &[RespValue]) -> ProtocolResult<BytesMut> {
        let array = RespValue::Array(Some(values.to_vec()));
        Self::encode(&array)
    }
}